package org.xtext.runtime.plan;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.formula.IFormula;
import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.EvaluateTermsVisitor;


public class IfStep implements IPlanStep {
	private IFormula guard;
	private IPlanStep ifStep;
	private IPlanStep elseStep;
	private int planState;
	
	public IfStep(IFormula guard, IPlanStep ifStep) {
		this(guard, ifStep, null);
	}
	
	public IfStep(IFormula guard, IPlanStep ifStep, IPlanStep elseStep) {
		this.guard = guard;
		this.ifStep = ifStep;
		this.elseStep = elseStep;
		planState = 0;
	}
	

	public String toString() {
		return toString("");
	}
	
	public String toString(String tabs) {
		String output = tabs + "if (" + guard + ") ...";
		if (elseStep != null) {
			output += " else ...";
		}
		return output;
	}

	public String toFullString(String tabs) {
		String output = tabs + "if (" + guard + ") " + 
					ifStep.toFullString(tabs);
		if (elseStep != null) {
			output += " else {\n" + elseStep.toFullString(tabs + IPlanStep.TAB) + "\n" + tabs + "}";
		}
		return output;
	}

	public IFormula guard() {
		return guard;
	}
	
	public IPlanStep ifStep() {
		return ifStep;
	}

	public IPlanStep elseStep() {
		return elseStep;
	}
	@Override
	public void handle(Agent agent, PlanStack planStack) {
		switch (planState) {
		case 0:
			List<Bindings> result = agent.query((IFormula) guard.accept(new EvaluateTermsVisitor(agent)));
			if (result != null) {
				planStack.back();
				if (result.isEmpty()) {
					planStack.addPlanStep(ifStep);
				} else {
					planStack.addPlanStep(ifStep.apply(result.get(0)));
				}

				planStack.getNextActivity().handle(agent, planStack);
				planState = 1;
			} else {
				if (elseStep != null) {
					planStack.back();
					planStack.addPlanStep(elseStep);
					planStack.getNextActivity().handle(agent, planStack);
					planState = 2;
				}
			}
			break;
		case 1:
		case 2:
			planState = 0;
		}
	}

	@Override
	public Set<Variable> variables() {
		Set<Variable> variables = new HashSet<Variable>();
		variables.addAll(guard.variables());
		variables.addAll(ifStep.variables());
		if (elseStep != null) variables.addAll(elseStep.variables());
		return variables;
	}

	@Override
	public IfStep copy(Map<String, Variable> vars) {
		if (elseStep == null) {
			return new IfStep(guard.copy(vars), ifStep.copy(vars));
		} else {
			return new IfStep(guard.copy(vars), ifStep.copy(vars), elseStep.copy(vars));
		}
	}

	@Override
	public IfStep apply(Bindings bindings) {
		guard.apply(bindings);
		ifStep.apply(bindings);
		if ( elseStep != null ) {
		   elseStep.apply(bindings);
		}
		return this;
	}

	@Override
	public void reset() {
	}
}
